package fun.tan90.easy.binlog.manager.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.mybatisflex.core.constant.SqlOperator;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryCondition;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import fun.tan90.easy.binlog.base.constants.EasyBinlogConstants;
import fun.tan90.easy.binlog.base.convention.Res;
import fun.tan90.easy.binlog.base.convention.enums.IErrorCode;
import fun.tan90.easy.binlog.base.convention.exception.ClientException;
import fun.tan90.easy.binlog.base.convention.page.SimplePage;
import fun.tan90.easy.binlog.manager.dataobject.mapper.BinlogOutputMapper;
import fun.tan90.easy.binlog.manager.dataobject.table.BinlogOutputTableDef;
import fun.tan90.easy.binlog.manager.dict.Dict;
import fun.tan90.easy.binlog.manager.event.DamiBusEvent;
import fun.tan90.easy.binlog.manager.model.cmd.BinlogOutputAddCmd;
import fun.tan90.easy.binlog.manager.model.cmd.BinlogOutputPageCmd;
import fun.tan90.easy.binlog.manager.model.vo.BinlogOutputInfoVo;
import fun.tan90.easy.binlog.manager.service.BinlogOutputService;
import fun.tan90.easy.binlog.manager.dataobject.BinlogOutput;
import fun.tan90.easy.binlog.manager.model.cmd.BinlogOutputUpdateCmd;
import fun.tan90.easy.binlog.manager.model.convert.BinlogOutputConvert;
import fun.tan90.easy.binlog.manager.model.vo.BinlogOutputPageVo;
import fun.tan90.easy.binlog.manager.service.BinlogWorkerService;
import org.noear.dami.Dami;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.*;

import static com.mybatisflex.core.query.QueryMethods.noCondition;

/**
 * 服务层实现。
 *
 * @author 陈浩杰
 * @since 2023-09-12
 */
@Service
public class BinlogOutputServiceImpl extends ServiceImpl<BinlogOutputMapper, BinlogOutput> implements BinlogOutputService {

    @Resource
    BinlogOutputConvert binlogOutputConvert;

    @Resource
    BinlogWorkerService binlogWorkerService;

    @Override
    public Res<SimplePage<BinlogOutputPageVo>> paging(BinlogOutputPageCmd pageCmd) {
        String outputType = pageCmd.getOutputType();
        String outputStatus = pageCmd.getOutputStatus();
        String outputName = pageCmd.getOutputName();

        QueryWrapper queryWrapper = QueryWrapper.create()
                .where(StringUtils.hasLength(outputType) ? BinlogOutputTableDef.BINLOG_OUTPUT.OUTPUT_TYPE.eq(outputType) : noCondition())
                .and(StringUtils.hasLength(outputStatus) ? BinlogOutputTableDef.BINLOG_OUTPUT.OUTPUT_STATUS.eq(outputStatus) : noCondition())
                .and(StringUtils.hasLength(outputName) ? BinlogOutputTableDef.BINLOG_OUTPUT.OUTPUT_NAME.like(outputName) : noCondition());

        Page<BinlogOutput> paginate = mapper.paginate(pageCmd.getPageIndex(), pageCmd.getPageSize(), pageCmd.getTotalRow(), queryWrapper);
        List<BinlogOutputPageVo> binlogOutputWayPageVos = binlogOutputConvert.dosToPageVos(paginate.getRecords());
        return Res.pageOf(paginate.getPageNumber(), paginate.getTotalRow(), binlogOutputWayPageVos);
    }

    @Override
    public BinlogOutputInfoVo info(String id) {
        return binlogOutputConvert.doToInfoVo(mapper.selectOneById(id));
    }

    @Override
    public String addBinlogOutput(BinlogOutputAddCmd addCmd) {
        BinlogOutput binlogOutput = binlogOutputConvert.addToDo(addCmd);
        String outputName = binlogOutput.getOutputName();
        long count = mapper.selectCountByCondition(QueryCondition.create(BinlogOutputTableDef.BINLOG_OUTPUT.OUTPUT_NAME, outputName));
        if (count > 0) {
            throw new ClientException(IErrorCode.BINLOG_1004009);
        }
        String outputCode = RandomUtil.randomString(RandomUtil.BASE_CHAR, 8);
        long checkOutputCode = 1;
        while (checkOutputCode > 0) {
            checkOutputCode = mapper.selectCountByQuery(QueryWrapper.create().where(BinlogOutputTableDef.BINLOG_OUTPUT.OUTPUT_CODE.eq(outputCode)));
            if (checkOutputCode == 0) {
                break;
            }
            outputCode = RandomUtil.randomString(RandomUtil.BASE_CHAR, 8);
        }
        binlogOutput.setOutputCode(outputCode);
        mapper.insert(binlogOutput);
        return binlogOutput.getId();
    }

    @Override
    public void updateBinlogOutput(BinlogOutputUpdateCmd updateCmd) {
        String id = updateCmd.getId();
        BinlogOutput dbBinlogOutput = mapper.selectOneById(id);
        if (Objects.isNull(dbBinlogOutput)) {
            return;
        }
        QueryCondition queryCondition = QueryCondition.create(BinlogOutputTableDef.BINLOG_OUTPUT.OUTPUT_NAME, updateCmd.getOutputName())
                .and(QueryCondition.create(BinlogOutputTableDef.BINLOG_OUTPUT.ID, SqlOperator.NOT_EQUALS, id));
        long count = mapper.selectCountByCondition(queryCondition);
        if (count > 0) {
            throw new ClientException(IErrorCode.BINLOG_1004009);
        }
        BinlogOutput binlogOutputWay = binlogOutputConvert.updateToDo(updateCmd);
        int updated = mapper.update(binlogOutputWay);
        if (updated > 0) {
            Dami.<String, Void>bus().send(DamiBusEvent.OUTPUT_CHANGE, dbBinlogOutput.getOutputCode());
        }
    }

    @Override
    public void switchBinlogOutputStatus(String id) {
        BinlogOutput dbBinlogOutput = mapper.selectOneById(id);
        if (Objects.isNull(dbBinlogOutput)) {
            return;
        }
        String outputStatus = "0".equals(dbBinlogOutput.getOutputStatus()) ? "1" : "0";
        int updated = mapper.update(BinlogOutput.builder().outputStatus(outputStatus).id(id).build());
        if (updated > 0) {
            Dami.<String, Void>bus().send(DamiBusEvent.OUTPUT_CHANGE, dbBinlogOutput.getOutputCode());
        }
    }

    @Override
    public void delBinlogOutput(String id) {
        BinlogOutput dbBinlogOutput = mapper.selectOneById(id);
        if (Objects.isNull(dbBinlogOutput)) {
            return;
        }
        int deleted = mapper.deleteById(id);
        if (deleted > 0) {
            Dami.<String, Void>bus().send(DamiBusEvent.OUTPUT_CHANGE, dbBinlogOutput.getOutputCode());
        }
    }

    @Override
    public String test(String outputType, String params) {
        String pluginType = Dict.dict(Dict.OUTPUT_TYPE, outputType);
        Map<String, String> resMap = binlogWorkerService.broadcastSendAndRequest(EasyBinlogConstants.OUTPUT_TEST, params, new HashMap<String, String>() {{
            put("pluginType", pluginType);
        }});
        Collection<String> testRes = resMap.values();
        long success = testRes.stream().filter("true"::equals).count();
        long error = testRes.stream().filter(res -> !"true".equals(res)).count();
        if (testRes.size() == success) {
            // 全部工作节点成功
            return "1";
        } else if (testRes.size() == error) {
            // 全部工作节点失败
            return "0";
        } else {
            // 部分工作节点成功
            return "2";
        }
    }
}